﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.FileProviders.Embedded;
using Microsoft.Extensions.FileProviders.Physical;
using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
using Microsoft.Extensions.Primitives;
using static System.Net.WebRequestMethods;

namespace VirtualFileSystem
{
    public class DictionaryEmbeddedFileProvider : AbsDictionaryFileProvider
    {
      
        public Assembly Assembly { get; }

        
        public string BaseNamespace { get; }

        public string? RequestPath { get; private set; }

        private DateTime lastModifyDate;

        protected override IDictionary<string, IFileInfo> Files => _files.Value;
        private readonly Lazy<Dictionary<string, IFileInfo>> _files;

        public DictionaryEmbeddedFileProvider([NotNull]Assembly assembly,string? requestPath = null, string? baseNamespace=null)
        {
            Assembly = assembly;
            BaseNamespace = baseNamespace ?? assembly.GetName().Name;
            RequestPath = requestPath;
            lastModifyDate = System.IO.File.GetLastWriteTimeUtc(Assembly.Location);
            _files = new Lazy<Dictionary<string, IFileInfo>>(GetAllFiles,true);
        }

        /// <summary>
        /// 将嵌入式虚拟文件映射到物理文件
        /// </summary>
        /// <param name="contentPath"></param>
        public void ReplaceEmbedToPhysical(string contentPath)
        {
            foreach(var file in _files.Value)
            {
                if (file.Value is EmbeddedResourceFileInfo embedded)
                {                    
                    var virtualFileName = file.Key;
                    if (string.IsNullOrEmpty(RequestPath) == false)
                    {
                        virtualFileName=file.Key[RequestPath.Length..];
                    }
                    string fileName = Path.Combine(contentPath, virtualFileName.TrimStart('/').Replace('/', Path.DirectorySeparatorChar));
                    var virtualPhysicalFileInfo = new VirtualPhysicalFileInfo(fileName);
                    if (virtualPhysicalFileInfo.Exists)
                    {
                        _files.Value[file.Key] = new VirtualPhysicalFileInfo(fileName);
                        //_files.Value[file.Key] = new PhysicalFileInfo(new FileInfo(fileName));  //此代码加载的物理文件无法取到最新的修改时间，文件大小也取不到最新的，导致数据变多时会截断，变少时运行时会崩溃。
                    }
                }
            }
        }

        private Dictionary<string, IFileInfo> GetAllFiles()
        {
            Dictionary<string, IFileInfo> files = new();
            var resourceNames = Assembly.GetManifestResourceNames();
            if (resourceNames.Length > 0)
            {
                files["/"] = new VirtualDirectoryFileInfo("/", "/", lastModifyDate);
                if (!string.IsNullOrEmpty(RequestPath))
                {
                    var reqPaths = RequestPath.Split('/', StringSplitOptions.RemoveEmptyEntries);
                    RequestPath = AppendDirectory(files, reqPaths);
                }
                foreach (var resourceName in Assembly.GetManifestResourceNames())
                {
                    if (resourceName.StartsWith(BaseNamespace) == false) continue;
                    var fullFileName = resourceName[(BaseNamespace.Length + 1)..];
                    var splitNames = fullFileName.Split(".");

                    var fullDirectoryPath=AppendDirectory(files, splitNames[..^2], RequestPath);                
                    var fileName = string.Join(".", splitNames[^2..]);
                    var key = fullDirectoryPath.ToLower() + "/" + fileName.ToLower();
                    files.Add(key, new EmbeddedResourceFileInfo(Assembly, resourceName, fileName, lastModifyDate));
                }
            }
            return files;
        }

        private string AppendDirectory(Dictionary<string, IFileInfo> files, string[] paths,string reqPath=null)
        {
            StringBuilder path = new StringBuilder(reqPath);
            for (var i = 0; i < paths.Length; i++)
            {
                path.Append("/" + paths[i]);
                string p = path.ToString().ToLower() + "/";
                files[p] = new VirtualDirectoryFileInfo(p, paths[i], lastModifyDate);
            }
            return path.ToString();
        }
    }
}